// https://www.acwing.com/problem/content/242/

#include <cstdio> // 添加此头文件以使用 printf
using namespace std;

const int MAX_ARRAY_SIZE = 50010; // 定义数组的最大大小

// p[i] 表示 i 的父节点, d[i] 表示从 i 到其父节点的关系类型 (0: 同类, 1: 被捕食, 2: 捕食)
int p[MAX_ARRAY_SIZE], d[MAX_ARRAY_SIZE];

// 查找并查集中的根节点，并进行路径压缩
int find(int x) {
    if (p[x] != x) { // 如果当前节点不是根节点
        int root = find(p[x]); // 递归找到根节点
        d[x] = (d[x] + d[p[x]]) % 3; // 更新距离
        p[x] = root; // 路径压缩，直接连接到根节点
    }
    return p[x]; // 返回根节点
}

int main() {
    int n, k;
    scanf("%d%d", &n, &k); // 输入动物数量 n 和说法的数量 k
    for (int i = 1; i <= n; i++) { // 初始化每个节点的父节点为自己
        p[i] = i;
        d[i] = 0; // 初始化距离为0
    }

    int ans = 0; // 记录假话的数量
    while (k--) { // 处理每一条说法
        int t, a, b;
        scanf("%d%d%d", &t, &a, &b); // 输入关系类型 t 和两个动物编号 a, b

        // 检查输入是否有效
        if (a > n || b > n || (t == 2 && a == b)) { // 如果编号超出范围或同类关系中 a 等于 b
            ans++;
            continue;
        }

        int rel = t - 1; // 直接计算关系类型，t=1时rel=0，t=2时rel=1

        int px = find(a);
        int py = find(b);

        if (px == py) { // 如果两个节点属于同一个集合
            // 检查当前关系是否满足条件
            if ((d[a] - d[b] + 3) % 3 != rel) {
                ans++; // 如果不满足则计数器加一
            }
        } else { // 如果两个节点不属于同一个集合
            // 合并两个集合，并更新距离
            p[px] = py;
            d[px] = (d[b] - d[a] + rel + 3) % 3;
        }
    }
    printf("%d\n", ans); // 使用 printf 输出假话的数量
    return 0;
}